<?php
/*
Mooha Server - SyncML Server written in PHP (http://mooha.net)
Copyright (C) 2009	Artem Bandur <artico.bandurini@gmail.com>
					Madcap BV (http://www.madcap.nl)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA	02110-1301, USA.
*/

require_once('inc/SyncmlException.php');

class WbxmlHelper {
	
	private $encoder;
	private $decoder;
	private $execPaths = array(
		'/usr/bin/',
		'/usr/local/bin/'
	);
	
	function __construct(){
		try {
			if (WBXML_USE_EXTERNAL && ($execPath = $this->_findExec('decoder'))) {
				$this->decoder = new WbxmlDecoderExt($execPath);
			}
			else {
				$this->decoder = new WbxmlDecoderPhp();
			}
			if (WBXML_USE_EXTERNAL && ($execPath = $this->_findExec('encoder'))) {
				$this->encoder = new WbxmlEncoderExt($execPath);
			}
			else {
				$this->encoder = new WbxmlEncoderPhp();
			}
		}
		catch (Exception $e) {
			throw new SyncmlException('Cannot run WbxmlHelper', 101, $e);
		}
	}
	
	public function decode($wbxml, $file = ''){
		try {
			if (empty($wbxml) && empty($file)) {
				throw new SyncmlException('Source (' . (empty($file) ? 'input' : $file) . ') is empty', 111);
			}
			try {
				return $this->decoder->decode($wbxml, $file);
			}
			catch (Exception $e) {
				throw new SyncmlException('Decoder failed', 112);
			}
		}
		catch (Exception $e) {
			throw new SyncmlException('Cannot decode WBXML', 110, $e);
		}
	}
	
	public function encode($xml, $file = ''){
		try {
			if (empty($xml) && empty($file)) {
				throw new SyncmlException('Source (' . (empty($file) ? 'input' : $file) . ') is empty', 121);
			}
			try {
				return $this->encoder->encode($xml, $file);
			}
			catch (Exception $e) {
				throw new SyncmlException('Encoder failed', 122);
			}
		}
		catch (Exception $e) {
			throw new SyncmlException('Cannot encode WBXML', 120, $e);
		}
	}
	
	private function _findExec($type){
		switch ($type) {
			case 'decoder':
				$t = 'wbxml2xml';
				break;
			case 'encoder':
				$t = 'xml2wbxml';
				break;
			default:
				$t = '';
				break;
		}
		if (!empty($t)) {
			foreach ($this->execPaths as $p){
				if (is_executable($p . $t)) {
					return $p;
				}
			}
		}
		return '';
	}
	
}


class WbxmlDecoderPhp {
	private $decoder;
	
	function __construct() {
		@include_once('XML/WBXML/Decoder.php');
		if (!class_exists('XML_WBXML_Decoder')) {
			throw new SyncmlException('Cannot run WbxmlDecoderPhp', 102);
		}
		$this->decoder = new XML_WBXML_Decoder();
	}
	
	public function decode($wbxml, $file = ''){
		if (!empty($file)) {
			$wbxml = file_get_contents($file);
		}
		return $this->decoder->decodeToString($wbxml);
	}
}


class WbxmlEncoderPhp {
	private $encoder;
	
	function __construct() {
		@include_once('XML/WBXML/Encoder.php');
		if (!class_exists('XML_WBXML_Encoder')) {
			throw new SyncmlException('Cannot run WbxmlEncoderPhp', 103);
		}
		$this->encoder = new XML_WBXML_Encoder();
	}
	
	public function encode($xml, $file = ''){
		if (!empty($file)) {
			$wbxml = file_get_contents($file);
		}
		return $this->encoder->encode($xml);
	}
}


class WbxmlDecoderExt {
	
	private $execPath = '';
	
	function __construct($execPath = ''){
		$this->execPath = $execPath;
	}
	
	public function decode($wbxml, $file = '') {
		$removeFile = false;
		if (empty($file)) {
			$file = tempnam('tmp', 'sml');
			file_put_contents($file, $wbxml);
			$removeFile = true;
		}
		$out = shell_exec($this->execPath . 'wbxml2xml -m 1 -o - "' . $file . '" 2>/dev/null');
		if ($removeFile) {
			unlink($file);
		}
		return $out;
	}

}


class WbxmlEncoderExt {
	
	private $execPath = '';
	
	function __construct($execPath = ''){
		$this->execPath = $execPath;
	}
	
	public function encode($xml, $file = ''){
		$removeFile = false;
		if (empty($file)) {
			$file = tempnam('tmp', 'sml');
			if (strpos($xml, '<!DOCTYPE') === false) {
				$DTDs = array(
					'1.0' => 'http://www.openmobilealliance.org/tech/affiliates/syncml/syncml_represent_v10_20001207.dtd',
					'1.1' => 'http://www.openmobilealliance.org/tech/affiliates/syncml/syncml_represent_v11_20020215.dtd',
					'1.2' => 'http://www.openmobilealliance.org/Tech/DTD/OMA-TS-SyncML_RepPro_DTD-V1_2-20050303-D.dtd'
				);
				$doctype = '<!DOCTYPE SyncML PUBLIC "-//SYNCML//DTD SyncML ' . SYNCML_VERSION . '//EN" "' . $DTDs[SYNCML_VERSION] . '">';
				$xml = str_replace('<?xml version="1.0"?>', '<?xml version="1.0"?>' . $doctype, $xml);
			}
			file_put_contents($file, $xml);
			$removeFile = true;
		}
		$out = shell_exec($this->execPath . 'xml2wbxml -v ' . WBXML_VERSION . ' -o - "' . $file . '" 2>/dev/null');
		if ($removeFile) {
			unlink($file);
		}
		return $out;
	}
	
}